/**
 * @author Andrzej Martynowicz
 * @version 0.66
 * 
 * Note: this library is under development. Some features may
 * slightly change in final version.
 * 
 * This library is redistributed under LGPL license.
 * See http://www.gnu.org/copyleft/lesser.html for details.
 * 
 */

  // =========================================== //
 //  Definition of GlassLayerPopupConstants     //
// =========================================== //

/**
 * @class This class holds some constants used later.
 * @private
 */
function GlassLayerPopupConstants() {}

/**
 * Represents the value of name attribute of the main div which defines the popup
 */
GlassLayerPopupConstants.MAIN_DIV_NAME = "glassLayerPopupDiv";

/**
 * Represents the prefix of id attribute of the main div which defines the popup
 */
GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX = "glassLayerPopup_";

/**
 * Represents the prefix of name attribute of the iframe (only for iframe popups)
 */
GlassLayerPopupConstants.IFRAME_NAME_PREFIX = "glassLayerPopupFrame_";



  // ========================================== //
 //  Definition of GlassLayerPopupsSharedData  //
// ========================================== //

/**
* @class This class is instantiated only once. The instance resides in top accessible window (see 
* {@link GlassLayerPopupUtils.getSharedData} to learn how this is achieved). Thanks to this we have always only one 
* and only one instance of GlassLayerPopupsSharedData in the application no matter how many nested popups we have 
* created (each nested popup created using GlassLayerPopup.showUrl() creates an iframe and we want to have only
* one instance of GlassLayerPopupsSharedData for all the frames). 
* 
* This class should not be instiated. It acts as data container only.
* 
* @private
*/
function GlassLayerPopupsSharedData() { 
	if (! GlassLayerPopupsSharedData.allowInstantiation) {
		throw "GlassLayerPopupsSharedData class should not be instaitiated. It is instantiated only internally by "
			+ " the library."
	}
	
	/**
	 * Associative array where key is id of the popup and value is a reference to
	 * the popup with the id. Stores all the popups.
	 * 
	 * WARNING: do not call directly to GlassLayerPopupsManager._popups, call to 
	 * GlassLayerPopupsManager.getPopups() instead since this will return correct
	 * 
	 * @private
	 */
	this.popups = new Object();
	
	/**
	 * This variable stores first available id for the new popup. When popup is created it requres id. Unless the
	 * id is provided as a parameter it is automatically generated by GlassLayerPopupsManager which reads 
	 * GlassLayerPopupsSharedData.currentPopupId and increases it by 1;
	 * 
	 * NOTE: if you need to assign your own id give it the number lesser than 1000
	 * and pass as an option to constructor of GlassLayerPopup
	 * @private
	 */
	this.nextPopupId  = 1000;
	
}

GlassLayerPopupsSharedData.allowInstantiation = false;
	
  // ====================================== //
 //  Definition of GlassLayerPopupUtils    //
//======================================= //


/**
 * This class contains various helper and utility methods
 * 
 * @constructor
 * @return
 */
function GlassLayerPopupUtils() {}

/**
 * This is dummy variable. Added just so that eclipse parser correctly parses
 * GlassLayerPopupUtils as an object. 
 * @private
 */
GlassLayerPopupUtils.sharedData = null;

/**
 * Returns and instance of GlassLayerPopupsManager
 */
GlassLayerPopupUtils.getSharedData = function() {
	if (GlassLayerPopupsManager.sharedData == null) {
		var topAccessibleWindow = GlassLayerPopupUtils.getTopAccessibleWindow();
		
		var sharedDataInstance = topAccessibleWindow.GlassLayerPopupsSharedDataInstance;
		if (sharedDataInstance == undefined) {
			// shared data instance was not initialized yet. We will do it now
			GlassLayerPopupsSharedData.allowInstantiation = true;
			topAccessibleWindow.GlassLayerPopupsSharedDataInstance = new GlassLayerPopupsSharedData(); 
			GlassLayerPopupsSharedData.allowInstantiation = false;
			
			sharedDataInstance = topAccessibleWindow.GlassLayerPopupsSharedDataInstance;
			
			if (sharedDataInstance == undefined) {
				// we should not get here... but if we do... argh.. let's use local instance, but this is not right
				// since this will not be shared instance at all
				
				GlassLayerPopupsSharedData.allowInstantiation = true;
				sharedDataInstance = new GlassLayerPopupsSharedData();
				GlassLayerPopupsSharedData.allowInstantiation = false;
			}
		}
		
		GlassLayerPopupUtils.sharedData = sharedDataInstance;
	}
	
	return GlassLayerPopupUtils.sharedData;

}
	
/**
 * Returns the width of the screen. For internal use only.
 * @private
 */
GlassLayerPopupUtils.getCanvasWidth = function() {
	var width = document.documentElement.clientWidth;
	
	if (width == 0 || width == undefined) {
		width = document.body.clientWidth; // window.innerWidth
	}
	
	if (width == 0 || width == undefined) {
		width = screen.width;
	}
	return width;
};

/**
 * Returns the height of the body. For internal use only.
 * @private
 */
GlassLayerPopupUtils.getCanvasHeight = function() {
	var canvasHeight = document.documentElement.clientHeight;

	if (! GlassLayerPopupUtils.isIEBrowser()) {
		// document.body.clientHeight returns size of visible area on IE and not canvas size
		if (canvasHeight == 0 || canvasHeight == undefined) { 
			canvasHeight = document.body.clientHeight; 
		}
		
		if (document.body.clientHeight != 0 && document.body.clientHeight != undefined && document.body.clientHeight > canvasHeight) {
			canvasHeight = document.body.clientHeight;
		}
		
	}

	if (canvasHeight == 0 || canvasHeight == undefined) { 
		canvasHeight = document.body.scrollHeight;
	} 
	
	return canvasHeight;

};

/**
 * Returns the height of page which is visible in browser. For internal use only.
 * @private
 */
GlassLayerPopupUtils.getVisibleAreaHeight = function() {
	var height = 0;
	
	if (GlassLayerPopupUtils.isIEBrowser()) { 
		height = document.body.clientHeight;
	} else if (GlassLayerPopupUtils.isFirefoxBrowser()) { 
		height = document.documentElement.clientHeight;

		if (document.body.clientHeight == window.innerHeight && window.innerHeight != 0 && window.innerHeight != undefined) { 
			// this is in case there is not enough text on the page to cover it all i.e. there is no
			// scrollbar in browser because the page is small. In this case we need to use document.body.clientHeight
			// because it will return the actual browser visible area size rather than document.documentElement.clientHeight
			// which will return height of content of the page
			height = document.body.clientHeight;
		}
	} else { // Opera, Safari
		height = document.body.clientHeight;
		
	}
	
	if (height == 0 || height == undefined) { // if there was problem to detect height we can rely on screen.height
		height = screen.height;
	} 

	return height;
};

/**
 * Returns height of whole area which can be seen and/or scrolled. This is the maximum of
 *  GlassLayerPopupUtils.getVisibleAreaHeight() and GlassLayerPopupUtils.getBodyHeight
 */
GlassLayerPopupUtils.getMaxAvailableHeight = function() {
	var bodyHeight = GlassLayerPopupUtils.getCanvasHeight();
	var vibleAreaHeight = GlassLayerPopupUtils.getVisibleAreaHeight();
	
	if (bodyHeight > vibleAreaHeight) {
		return bodyHeight;
	} else {
		return vibleAreaHeight;
	}
};



/**
 * Returns position of horizontal window scrollbar. For internal use only.
 * @private
 */
GlassLayerPopupUtils.getScrollbarXOffset = function() {
	return window.pageXOffset ? window.pageXOffset : document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft;
};

/**
 * Returns position of vertical window scrollbar. For internal use only.
 * @private
 */
GlassLayerPopupUtils.getScrollbarYOffset = function() {
	return window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
};

/**
 * Calculates y offset for element which should be centered in currently visible area
 * 
 * @private
 */
GlassLayerPopupUtils.getCenteredYOffset = function(height) {
	return Math.round(GlassLayerPopupUtils.getScrollbarYOffset() + (GlassLayerPopupUtils.getVisibleAreaHeight() / 2  - height / 2));
}

/**
 * Returns the top window where application has access
 * @private
 */
GlassLayerPopupUtils.getTopAccessibleWindow = function() {
//	if (window == top) {
//		// we are in top window so current one (top) is of course the
//		// top accessible one
//		return window;
//	}
	
	var topAccessible = window;
	var parentWindow = null;
	var canAccessParent = true;
	
	while(canAccessParent) {
		try {
			parentWindow = topAccessible.parent;
			if (parentWindow == topAccessible) {
				// if parent points to current frame than the current frame does
				// not have parent and it's top one
				canAccessParent = false;
			} else {
				// try to modify the frame
				parentWindow.glassLayerPopupAccessTest = function() {};
				topAccessible = parentWindow;
			}
		} catch (e) {
			// there was no parrent window or we could not access the parnet
			// so we should stop looping since current window is top accesible
			canAccessParent = false;
		}
	}
	return topAccessible;
};


/**
 * @deprecated call {@link GlassLayerPopupsManager} statically
 */
GlassLayerPopupUtils.getPopupsManager = function() {
	return GlassLayerPopupsManager;
};

/**
 * @deprecated use {@link GlassLayerPopupsManager.isInsideIframePopup} instead 
 */
GlassLayerPopupUtils.isInsideIframePopup = function() {
	return GlassLayerPopupsManager.isInsideIframePopup();
}

/**
 * @deprecated use {@link GlassLayerPopupsManager.getCurrentIframePopup} instead 
 */
GlassLayerPopupUtils.getCurrentIframePopup = function() {
	return GlassLayerPopupsManager.getCurrentIframePopup();
};

/**
 * @deprecated use {@link GlassLayerPopupsManager.getHtmlPopupWithElement} instead 
 */
GlassLayerPopupUtils.getHtmlPopupWithElement = function(element) {
	return GlassLayerPopupsManager.getHtmlPopupWithElement(element);
}

/**
 *	Checks if current browser is Internet Explorer.
 *	@private 
 */
GlassLayerPopupUtils.isIEBrowser = function() {
	//return /MSIE (\d+\.\d+);/.test(navigator.userAgent);
	return window.ActiveXObject ? true : false;
};

/**
 *	Checks if current browser is Firefox.
 *	@private 
 */
GlassLayerPopupUtils.isFirefoxBrowser = function() {
	return navigator.userAgent.indexOf("Firefox")!=-1;
}



/**
 * This is private helper function and can not be accessed by outside world.
 * It parses the number from string. The parsing begins at possition defined
 * by beginIndex parameter
 * 
 * @access private
 */
GlassLayerPopupUtils._parseIdFromString = function(str, beginIndex) {
	var firstChar = str.charAt(beginIndex);
	if (isNaN(firstChar)) {
		throw "Can not parse id out of " + str + " The char "
			+ "at index " + beginIndex + " is not numeric";
	}
	
	var ret = "" + str.charAt(beginIndex);
	
	for (var i = beginIndex + 1; i < str.length; i++) {
		var curChar = str.charAt(i);
		if (isNaN(curChar)) {
			break;
		}
		ret += curChar;
	}
	
	return ret;
	
};

	
/**
 * Each main div of GlassLayerPopup has assigned DOM identifier which has following
 * outlook: GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX + uniqueId + "_mainDiv".
 * 
 * This method retrives the uniqueId from above string
 * 
 * @private
 * @access package
 * @return the id of the popup as string
 */
GlassLayerPopupUtils.parseIdFromMainDivId = function(glassLayerPopupIdString) {
	// If GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX == "glassLayerPopup_" 
	// than this has 16 characters, so the 17th (glassLayerPopupIdString[16])
	// stands for the first character of id
	var beginIndex = GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX.length;
	return GlassLayerPopupUtils._parseIdFromString(glassLayerPopupIdString, beginIndex);
	
};

/**
 * Each iframe of GlassLayerPopup has assigned DOM name attribute which has following
 * outlook: GlassLayerPopupConstants.IFRAME_NAME_PREFIX + uniqueId 
 * 
 * This method retrives the uniqueId from above string
 * 
 * @private
 * @access package
 * @return the id of the popup as string
 */
GlassLayerPopupUtils.parseIdFromPopupIframeName = function(iframeName) {
	// If GlassLayerPopupConstants.IFRAME_NAME_PREFIX == "glassLayerPopupFrame_" 
	// than this has 21 characters, so the 22nd (glassLayerPopupIdString[21])
	// stands for the first character of id
	var beginIndex = GlassLayerPopupConstants.IFRAME_NAME_PREFIX.length;
	return GlassLayerPopupUtils._parseIdFromString(iframeName, beginIndex);
	
};

/**
 * @private
 * @access package
 */
GlassLayerPopupUtils.getContentDivByMainDiv = function(mainDivRef) {
	return mainDivRef.childNodes[1];
}

/**
 * @private
 * @access package
 */
GlassLayerPopupUtils.getTopbarDivByContentDiv = function(contentDivRef) {
	return contentDivRef.firstChild;
}

/**
 * Returns reference to iframe element or null in case contentDiv does not
 * contain glassLayerPopup iframe
 * 
 * @private
 * @access package
 */
GlassLayerPopupUtils.getPopupIframeByContentDiv = function(contentDivRef) {
	if (contentDivRef.childNodes.length <= 1 || 
		contentDivRef.childNodes[1].nodeName.toLowerCase() != 'iframe' ||
		contentDivRef.childNodes[1].name.indexOf(GlassLayerPopupConstants.IFRAME_NAME_PREFIX) !== 0) {
		return null;
	}
	
	return contentDivRef.childNodes[1];
}



  // ================================= //
 //  Definition of GlassLayerPopup    //
//================================== //


/**
 * Constructs new instance of GlassLayerPopup
 * 
 * The options:
 * <ul>
 * 	<li> id - the id of the popup - if you set this manually you should set
 * 		      it to the number less than 1000 since 1000 is the first auto
 * 			  generated number</li>
 *  <li> width - the initial width of the popup</li>
 *  <li> height - the initial height of the popup</li>
 *  <li> allowClose - set it to true to enable close button</li>
 *  <li> allowMaximize - set it to true to enable maximize button</li>
 *  <li> scrolling - no|yes|auto - set it to enable or disable scrolling if
 *  			     if content does not fit the window</li>
 *  <li> domDocument - the document element to which the popup will be attached.
 *  		By default the current document is used, but you might want
 *  		to pass here another document. For example if you created popup using
 *  		showUrl and you want to display the anotoher popup from inisde this
 *  		by defaut this will result in popup "inside popup". To overcome this
 *  		(if you want the second popup not to be displayed inside the current
 *  		popup) you can pass here top.document (or the document to which the 
 *  		current popup was attached to) 
 *  <li> styleClassPrefix - the prefix of attribute class of all items in popup.
 *  		It can be used used to customize outlook of popup. By default the 
 *  		preffix is 'glassLayerPopup_', so for example the content div will
 *  		have class attribute 'glassLayerPopup_content_div' </li>
 *  <li> closeButtonHtml - the html which will be displayed as a close button. 
 *  		Defaults to [x].</li>
 *  <li> closeButtonImg - if set overrides the closeButtonHtml. The value describes
 *  		the source of the image (so the attribute of src tag) which should be
 *  		displayed as button which closes window</li>
 *  <li> maximizeButtonHtml - the html which will be displayed as a maximize button
 *  		Defaults to [M].</li>
 *  <li> maximizeButtonImg - if set overrides the maximizeButtonHtml. The value 
 *  		describes the source of the image (so the attribute of src tag) which 
 *  		should be displayed as button which maximizes window</li>
 *  <li> topButtonsSpacerHtml - the html which should be used as a spacer which
 *  		separates the buttons on top bar (close button, maximize button).
 *  		Defaults to &nbsp;</li>
 *  <li> closeButtonOnClick - a function which should be called when the close button 
 *  		(on topbar) is clicked. This function is called before the actual close()
 *  		method is called on popup so it is called before any beforeCloseListeners
 *  		or afterCloseListeners are called</li>
 *  <li> closeButtonOnClickParams - if you whish to use closeButtonOnClick option than
 *  		you can use closeButtonOnClickParams option to pass parameters to the
 *  		function defined by closeButtonOnClick. The parameters should be passed
 *  		inside array, example: 
 *  		new GlassLayerPopup({closeButtonOnClick : function(text) { alert(text);}, closeButtonOnClickParams : new Array("Hello world")}) </li>
 *  <li> contentStyle - adds extra style for the content of the popup. There are
 *  		some limitations here: you are not allowed to pass here following style
 *  		attributes: left, top, position, width, height. Note: width and height
 *  		can be passed via width and height option. Defaults to:
 *  		"border-style: solid; border-width: 1px;"</li>
 *  <li> topbarStyle - adds extra style for the topbar of the popup (topbar is the bar
 *  		there maximize and close buttons are placed)
 *  <li> modal - true or false - decides whether current window should be considered
 *  		modal shich means that user can click only on elements of the popup and
 *  		all the other elements on the page are blocked. Defaults to false.
 *  <li> modalBgStyle - adds extra style for the whole page except for the popup. It's
 *  		role is to cover all the visible area not occupied by the popup with layer
 *  		with style defined by this option. Defaults to: 'color: #f1eedf'
 *  <li> modalBgOpacity - then modalBgStyle is not empty than this option defines the
 *  		opacity of the background. Should be set to value between 0 and 1. Defaults
 *  		to 0.5.
 * </ul>
 * 
 * @constructor
 * @param options array of options
 * @return
 */
function GlassLayerPopup(options) {
	
	// =================== constructor ===================== //
	var defaultOptions = {
			id : 0,
			width : 400,	
			height : 300,
			allowClose : true,
			allowMaximize : true,
			scrolling : 'no',
			domDocument : document,
			styleClassPrefix : 'glassLayerPopup_',
			closeButtonHtml : '[x]',
			closeButtonImg : '',
			maximizeButtonHtml : '[M]',
			maximizeButtonImg : '',
			topButtonsSpacerHtml : '&nbsp;',
			closeButtonOnClick : null,
			closeButtonOnClickParams : null,
			contentStyle : 'border-style: solid; border-width: 1px;',
			topbarStyle : '',
			modal : false,
			modalBgStyle : 'background-color: #000000', 
			modalBgOpacity : 0.5
	};
	
	defaultOptions.id = GlassLayerPopupsManager.getNextId(); 
	
	if (options != undefined) {
		// options were passed so we will take default values only for those which were 
		// not passed 
		for (var key in options) {
			defaultOptions[key] = options[key];
		}
	}
	
	this.options = defaultOptions;
	
	// validate the options
	if (this.options.topbarStyle.indexOf('"') != -1) {
		throw 'GlassLayerPopup: The option topbarStyle can not contain " (double quote) character';
	}
	if (this.options.contentStyle.indexOf('"') != -1) {
		throw 'GlassLayerPopup: The option contentStyle can not contain " (double quote) character';
	}
	if (this.options.modalBgStyle.indexOf('"') != -1) {
		throw 'GlassLayerPopup: The option modalBgStyle can not contain " (double quote) character';
	}

	/**
	 * The value of following property should be used as a prefix of all identifiers for this popup
	 * @private
	 */
	this.domPrefix = GlassLayerPopupsManager.getDomId(this.options.id);
	this.popupDiv = null; // popupDiv this will be created by this this._show
	this.isIframePopup = null; // isIframePopup will be set by call to showUrl or showHtml
	this.listeners = {};
	
	/**
	 * Contains reference to parent iframe popup unless current popup is top popup.
	 * @private
	 */
	this.parentPopup = null;
	
	if (GlassLayerPopupUtils.isInsideIframePopup()) {
		this.parentPopup = GlassLayerPopupUtils.getCurrentIframePopup();
	}
	
	/**
	 * Contains reference to window inside which this popup is shown.
	 * @private
	 */
	this.contextWindow = window; // store reference to window inside which current popup will be shown
	
	/**
	 * Returns reference to window inside which this popup is shown. For iframe popup this will be the parent
	 * window of iframe popup, for html popup this will be the windows inside which the html is shown.
	 */
	this.getContextWindow = function() {
		return this.contextWindow;
	}	
	
	/**
	 * When call to showUrl is made this property is set to url which this
	 * iframe if this is iframe popup. For html popup it remains null. 
	 * @private
	 */
	this.iframePopupInitUrl = null;

//	/**
//	 * Contains the current status of the popup. Possible statuses are SHOWING, SHOWN, CLOSING
//	 * @private
//	 */
//	this.state = 'SHOWING';

	GlassLayerPopupsManager.popupCreated(this.options.id, this);
	// =============== end of constructor =================== //
	
	// =============== methods definition =================== //
	
	/**
	 * Returns the value of option passed as paremeter
	 * @param option a string identifying the option 
	 * @private
	 * @access package
	 */
	this.getOptionValue = function(option) {
		var optionValue = null;
		eval('optionValue = this.options.' + option);
		return optionValue;
	}
	
	/**
	 * Returns the dom document to which this popup is attached
	 * @private
	 * @access package
	 */
	this.getDomDocument = function() {
		return this.options.domDocument;
	}

	/**
	 * Returns the refference to the main div which builds the popup
	 * @private
	 * @access package
	 */
	this.getMainDiv = function() {
		return this.popupDiv;
	}

	/**
	 * Returns the id generated or manually assigned to given popup
	 */
	this.getId = function() {
		return this.options.id;
	}

	/**
	 * Displays the popup. The content of the popup is filled with given html 
	 */
	this.showHtml = function (html) {
		this.isIframePopup = false;
		this._show(html);
//		this.state = 'SHOWN';
	};
	
	/**
	 * Returns parent iframe popup or null if this popup does not have iframe popup parent.
	 * 
	 * TODO: add this to tuorial
	 */
	this.getParentPopup = function() {
		return this.parentPopup;
	}
	
	/**
	 * In case of iframe popup this method sets correct size of iframe and
	 * should be called in case size of the whole popup changed and we
	 * need to adjust size of the iframe
	 * @private
	 */
	this.adjustIframeSize = function() {
		var mainDiv = this.popupDiv;
		var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(mainDiv);
		var topbarDiv = GlassLayerPopupUtils.getTopbarDivByContentDiv(contentDiv);
		var iframe = GlassLayerPopupUtils.getPopupIframeByContentDiv(contentDiv);
		
		var contentDivHeight = contentDiv.clientHeight;
		var topbarHeight = topbarDiv.offsetHeight;
		var height = contentDivHeight - topbarHeight;
		var width = contentDiv.clientWidth;
		
		iframe.style.height = '' + height + 'px';
		iframe.style.width = '' + width + 'px';
	}
	
	/**
	 * Displays the popup. The content of the popup is loaded from given url
	 * using iframe. 
	 */
	this.showUrl = function (url) {
		this.isIframePopup = true;
		
		var iframeName = GlassLayerPopupConstants.IFRAME_NAME_PREFIX + this.options.id;
		
		// Temoraryly calculate the approximate dimensions of the frame.
		// They will be recalculated as soon as the popup is show. See below. 
		var width  = Math.round(0.95 * this.options.width)
		var height = Math.round(0.95 * this.options.height) - 25; // -25 - predicted height of topbar (with close and minmize buttons)
		
		var content = 
			'<iframe scrolling="'+ this.options.scrolling +'" frameborder="0" name="' + iframeName + '" '
			+	'style="'
			+		'width: ' + width + 'px; '
			+		'height: ' + height + 'px; '
      +  'z-index: 10000;'
			+ 	'"' 
			+ '></iframe>'
			;
		this._show(content);
		window.frames[iframeName].window.location = url; // set the desired url
		
		this.iframePopupInitUrl = url;
		
		this.adjustIframeSize();
//		this.state = 'SHOWN';
	};
	
	/**
	 * Shows modal background which is effects in blocking access to all visible
	 * elements on the screen.
	 */
	this._getModalBackgroundDivHtml = function() {
		if (! this.options.modal) {
			// if popup is not modal we don't cover the page with modal background
			// therefore we will show empty div 
			return '<div></div>';
		}
		
		var width = GlassLayerPopupUtils.getCanvasWidth();
		var height = GlassLayerPopupUtils.getMaxAvailableHeight();
		
		var bgStyle = this.options.modalBgStyle;
		if (bgStyle != '') {
			bgStyle += ';'; 
			if (GlassLayerPopupUtils.isIEBrowser()) {
				bgStyle += 'filter: alpha(opacity='+ (this.options.modalBgOpacity * 100) +');';
			} else {
				bgStyle += 'opacity:'+ this.options.modalBgOpacity +';';
			}
		}
		
		var html = '<div style="'
			+ 'position: absolute; ' 
			+ 'top: 0px; '
			+ 'left: 0px; '
			+ 'height: ' + height + 'px; '
			+ 'width: ' + width + 'px; '
			+ 'height: ' + height + 'px; '
			+ bgStyle
			+ '" '
			+ '></div>'

		return html;	
	}
	
	/**
	 * This is private function. Do not call it because the inner implementation
	 * may change. Use showUrl() or showContent() instead
	 */
	this._show = function (content) {
		var domDocument = this.options.domDocument;		
		
		var popupDiv = domDocument.createElement('div');
		this.popupDiv = popupDiv;
		
		popupDiv.setAttribute("name", GlassLayerPopupConstants.MAIN_DIV_NAME);
		popupDiv.setAttribute("id", this.domPrefix + "_mainDiv");
		
		// so some caluclations before used to determine the location of the div
		var left = Math.round(GlassLayerPopupUtils.getCanvasWidth() / 2 - this.options.width / 2); 
		var top = GlassLayerPopupUtils.getCenteredYOffset(this.options.height);
		
		if (left <= 0) left = 10;
		if (top <= 0) top = 10;
		
		var closeButtonHtml = this.options.closeButtonHtml;
		if (this.options.closeButtonImg != '') { // closeButtonImg overrides the closeButtonHtml
			closeButtonHtml = '<img src="'+ this.options.closeButtonImg +'" border="0" class="' + this.options.styleClassPrefix + 'close_button_img" />'
		}
		
		var maximizeButtonHtml = this.options.maximizeButtonHtml;
		if (this.options.maximizeButtonImg != '') { // maximizeButtonImg overrides the maximizeButtonHtml
			maximizeButtonHtml = '<img src="'+ this.options.maximizeButtonImg +'" border="0" class="' + this.options.styleClassPrefix + 'maximize_button_img" />'
		}
		
		var topbarDivStyle = (this.options.topbarStyle == '' ? 'text-align: right' : this.options.topbarStyle);  
		var contentDivExtraStyle = (this.options.contentStyle == '' ? '' : this.options.contentStyle);  
		
		popupDiv.innerHTML =
			this._getModalBackgroundDivHtml()
			+ '<div style="'
			+ contentDivExtraStyle + '; '
			+ 'width: ' + this.options.width + 'px; '
			+ 'height: ' + this.options.height + 'px; '
			+ 'position: absolute; '
			+ 'left: ' + left + 'px; '
			+ 'top: ' + top + 'px; '
			+ '"'
			+ 'class="' + this.options.styleClassPrefix + 'content_div"'
			+ '>'
			+ '<div id="' + this.domPrefix + '_topButtonsDiv" style="'+ topbarDivStyle +'" class="' + this.options.styleClassPrefix + 'topbar_div">'
			+ (this.options.allowMaximize 
				 	? '<a id="' + this.domPrefix + '_topButtonMaximize" +  '
				 		+ 'style="color: black; font-weight: bold; text-decoration: none" '
						+ 'href="javascript:void(0)">'+ maximizeButtonHtml +'</a>'
						+ this.options.topButtonsSpacerHtml
				 	: '')
			+ (this.options.allowClose 
			 	? '<a id="' + this.domPrefix + '_topButtonClose" +  '
			 		+ 'style="color: black; font-weight: bold; text-decoration: none" '
					+ 'href="javascript:void(0)">'+ closeButtonHtml +'</a>'
					+ this.options.topButtonsSpacerHtml
			 	: '')
			+ '</div>'
			+ content
			+ '</div>'
			;
		
		var body = domDocument.getElementsByTagName('body')[0];
		body.appendChild(popupDiv);
		
		// Now we will set the actions for all the buttons in the top bar
		if (this.options.allowMaximize) {
			var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(popupDiv);
			var topButtonsDiv = GlassLayerPopupUtils.getTopbarDivByContentDiv(contentDiv);
			
			var maximizeLink = GlassLayerPopupDOMUtils.findChildWithId(topButtonsDiv, this.domPrefix + '_topButtonMaximize');
			
			maximizeLink.onclick = function() {
				// beacause this function will be fired as onclick event of closeLink 
				// "this" will point to the maximizeLink itself (so to the <a href="..."> 
				// element
				var maximizeLinkElem = this; 
				
				// mainDiv is the main div of the popup (it is the actual popup itself) 
				var mainDiv = maximizeLinkElem.parentNode.parentNode.parentNode;
				var mainDivId = mainDiv.id;
				var popupId = GlassLayerPopupUtils.parseIdFromMainDivId(mainDivId);
				
				GlassLayerPopupsManager.maximizePopup(popupId);
			};
		};
		
		
		if (this.options.allowClose) {
			var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(popupDiv);
			var topButtonsDiv = GlassLayerPopupUtils.getTopbarDivByContentDiv(contentDiv);
			
			var closeLink = GlassLayerPopupDOMUtils.findChildWithId(topButtonsDiv, this.domPrefix + '_topButtonClose');
			
			closeLink.onclick = function() {
				// beacause this function will be fired as onclick event of closeLink 
				// "this" will point to the closeLink itself (so to the <a href="..."> 
				// element
				var closeLinkElem = this;
				
				// mainDiv is the main div of the popup (it is the actual popup itself) 
				var mainDiv = closeLinkElem.parentNode.parentNode.parentNode;
				var mainDivId = mainDiv.id;
				var popupId = GlassLayerPopupUtils.parseIdFromMainDivId(mainDivId);
				
				var popup = GlassLayerPopupsManager.getPopup(popupId);
				
				var onCloseCallback = popup.getOptionValue('closeButtonOnClick');
				var onCloseCallbackParams = popup.getOptionValue('closeButtonOnClickParams');

				if (onCloseCallback != null) {
					var callbackParams = (onCloseCallbackParams == null ? new Array() : onCloseCallbackParams);
					onCloseCallback.apply(onCloseCallback, callbackParams);
				}

				popup.close();
				
			};
		};
		
		// as a final step we will notify the manager that the popup has been shown
		GlassLayerPopupsManager.popupShown(this.options.id, this);
	};
	
	/**
	 * A function passed as the argrument will be called just before the popup is closed
	 */
	this.addBeforeCloseListener = function(callback) {
		if (this.listeners.beforeClose == undefined) {
			this.listeners.beforeClose = new Array();
		}
		this.listeners.beforeClose.push(callback);
	}
	
	/**
	 * A function passed as the argrument will be called right after the popup is closed
	 */
	this.addAfterCloseListener = function(callback) {
		if (this.listeners.afterClose == undefined) {
			this.listeners.afterClose = new Array();
		}
		this.listeners.afterClose.push(callback);
	}
	
	/**
	 * listenerType is the index of associative array this.listeners. So for example
	 * if index is onClose than all this.listerners.onClose will be called
	 * 
	 * @private
	 */
	this.callListeners = function(listenerType) {
		var listeners;
		eval('listeners = this.listeners.'+ listenerType);
		
		if (listeners != undefined) {
			for (var i=0; i<listeners.length; i++) {
				listeners[i].call(listeners[i], this);
			}
		}
	}
	
	/**
	 * Closes this popup
	 * @access public
	 */
	this.close = function() { 
//		if (this.state == 'CLOSING') {
//			throw "Trying to close popup which already is closing. You probably tried to call close() twice";
//		}
//		this.state = 'CLOSING';
		
		this.callListeners('beforeClose');
		
		var mainDiv = this.popupDiv;
		var domDocument = this.options.domDocument;

		var body = domDocument.getElementsByTagName('body')[0];
		body.removeChild(mainDiv);
		
		GlassLayerPopupsManager.popupClosed(this.options.id);
		
		this.callListeners('afterClose');
	}
	
	
	/**
	 * Closes this popup
	 * @access public
	 */
	this.maximize = function() {
		var mainDiv = this.popupDiv;
		var contentDiv = GlassLayerPopupUtils.getContentDivByMainDiv(mainDiv);
		
		var borderWidth = contentDiv.offsetWidth - contentDiv.clientWidth;
		var borderHeight = contentDiv.offsetHeight - contentDiv.clientHeight;
		
		var newWidth = GlassLayerPopupUtils.getCanvasWidth() - borderWidth;
		var newHeight = GlassLayerPopupUtils.getCanvasHeight() - borderHeight;
		var newLeft = 0;
		var newTop = 0;
		
		contentDiv.style.width = newWidth + 'px';
		contentDiv.style.height = newHeight + 'px';
		contentDiv.style.left = newLeft + 'px';
		contentDiv.style.top = newTop + 'px';
		
		// now if this is iframe popup (shwn using GlassLayerPopup.showUrl())
		// we need to resize the iframe with content as well
		if(this.isIframePopup) {
			this.adjustIframeSize();
		}
	}
	
	/**
	 * Refreshes this popup. When called on iframe popup results in reloading the iframe window. Currently does
	 * not have effect on html popup.
	 * 
	 * TODO: add this to tutorial
	 */
	this.refresh = function() {
		if (! this.isIframePopup) {
			// refreshing html popup is not supported yet.... it is not clear what should happen in such case (?)
			return;
		}
		
		var iframeWindowName = GlassLayerPopupConstants.IFRAME_NAME_PREFIX + this.options.id;
		this.contextWindow.frames[iframeWindowName].window.location.reload();
	}
	
	/**
	 * This method refreshes this iframe popup and ensures that the popup is
	 * loaded with it's original url - the url which was passed as a parameter
	 * to showUrl() method. This is useful in case there is a possibility that 
	 * the url of the iframe was changed.
	 * 
	 * TODO: document this in tutorial
	 */
	this.refreshToInitUrl = function() {
		var iframeWindowName = GlassLayerPopupConstants.IFRAME_NAME_PREFIX + this.options.id;
		var currentLocation = this.contextWindow.frames[iframeWindowName].window.location;
		
		if (this.iframePopupInitUrl == currentLocation) {
			this.refresh();
		} else { 
			this.contextWindow.frames[iframeWindowName].window.location = this.iframePopupInitUrl;
		}
	}
	
	/**
	 * Returns string representation of this popup containing the id of the popup.
	 * 
	 * NOTE: this is toString() method so do not expect that this method will return the same string when
	 * the library will be updated. This is debug method not a business method. 
	 *
	 * @access public
	 */
	this.toString = function() {
		return '[GlassLayerPopup id: '+ this.options.id +']';
	};
	
};




// ======================================= //
//  Definition of GlassLayerPopupsManager  //
//======================================== //

/**
 * @class This class contains functions which operates on one or many popups.
 * Note: Call the methods in static way and don't instantiate it.
 *
 */
function GlassLayerPopupsManager() {}

/**
 * Dummy variable which allows eclipse to parse the GlassLayerPopupsManager as class correctly
 * @private
 */
GlassLayerPopupsManager.dummy = 0;

/**
 * This method will act as a counter of all instances of GlassLayerPopup. Its
 * role is to create automathic numbering of all popups. The first assigned 
 * number is 1001. 
 * 
 * See {@link GlassLayerPopupsSharedData#currentId} for more info
 * 
 * NOTE: if you need to assign your own id give it the number lesser than 1000
 * and pass as an option to constructor of GlassLayerPopup

 * @private
 * @access package
 */
GlassLayerPopupsManager.getNextId = function() {
	var sharedData = GlassLayerPopupUtils.getSharedData();
	
	var nextId = sharedData.nextPopupId;
	sharedData.nextPopupId = sharedData.nextPopupId + 1;
	
	return nextId;
};

/**
 * Returns reference to popup with given id 
 * 
 * @param popupId
 * @return
 */
GlassLayerPopupsManager.getPopup = function(popupId) {
	var sharedData = GlassLayerPopupUtils.getSharedData();
	return sharedData.popups[popupId];
};


/**
 * Returns DOM unique id for given popupId. The popupId is the numeric unique
 * identifier of popup generated by GlassLayerPopupsManager.getNextId()
 * 
 * @param popupId
 * @private
 * @access package
 */
GlassLayerPopupsManager.getDomId = function(popupId) {
	return GlassLayerPopupConstants.MAIN_DIV_ID_PREFIX + popupId;	
};

/**
 * This is listener method. It is called by constructor of GlassLayerPopup to notify
 * the manager that the popup with given id has been created. The manager  
 * saves the reference to the popup
 * 
 * @private
 * @access package
 */
GlassLayerPopupsManager.popupCreated = function(popupId, popupInstance) {
	var sharedData = GlassLayerPopupUtils.getSharedData();
	sharedData.popups[popupId] = popupInstance;
}

/**
 * This is listener method. It is called by GlassLayerPopup._show() to notify
 * the manager that the popup with given id has been shown. The manager 
 * saves the reference to the the popup. <br/><br/>
 * 
 * NOTE: This might seem like a duplicate of GlassLayerPopupsManager.popupCreated() method. However this
 * listeners is called every time when the popup is shown, so allows manger to be aware of it even when it
 * is shown again. Consider following code:<br/><br/>
 * 
 * 
 * var popup = new GlassLayerPopup(); <i>// will issue a call to GlassLayerPopupsManager.popupCreated() which adds the reference to popup</i><br/>
 * popup.showHtml("Hello world!"); <i>// will issue a call to GlassLayerPopupsManager.popupShown() which replaces the reference added in previous step</i><br/>
 * popup.close(); <i>// will issue a call to GlassLayerPopupsManager.popupClosed() which removes the reference to popup</i><br/>
 * popup.showHtml("Hello again!"); <i>// will issue another call to GlassLayerPopupsManager.popupShown() and adds again the reference that has been removed while calling close()</i><br/>
 * 
 * 
 * @private
 * @access package
 */
GlassLayerPopupsManager.popupShown = function(popupId, popupInstance) {
	var sharedData = GlassLayerPopupUtils.getSharedData();
	sharedData.popups[popupId] = popupInstance;
}

/**
 * This is listener method. It is  called by GlassLayerPopup.close() method to notify
 * the manager that the popup has been closed. The manager assumes that it can
 * remove reference to this popup from all popups which it manages
 * 
 * @private
 * @access package
 */
GlassLayerPopupsManager.popupClosed= function(popupId) {
	var sharedData = GlassLayerPopupUtils.getSharedData();
	delete sharedData.popups[popupId];
}

/**
 * Asks a popup with given id to close
 */
GlassLayerPopupsManager.closePopup = function(popupId) {
	var sharedData = GlassLayerPopupUtils.getSharedData();
	var popup = sharedData.popups[popupId];
	popup.close();
}

/**
 * Asks a popup with given id to close and reloads the parent window. If parameter is passed parent window
 * will be reloaded to given url, otherwise window will be reloaded to it's current url.
 * 
 */
GlassLayerPopupsManager.closePopupAndReloadParent = function(popupId, reloadUrl) {
	var manager = GlassLayerPopupsManager.getPopup(popupId);
	
	popup.addAfterCloseListener(function(popup) {
		var contextWindow = popup.getContextWindow();

		if (reloadUrl == undefined) {
			contextWindow.location.reload();
		} else {
			contextWindow.location = reloadUrl;
		}
	});

	popup.close();
	
}

/**
 * Asks current ifrane popup to close and reloads the parent window. If parameter is passed parent window
 * will be reloaded to given url, otherwise window will be reloaded to it's current url.
 * 
 */
GlassLayerPopupsManager.closeCurrentIframePopupAndReloadParent = function(reloadUrl) {
	var popup = GlassLayerPopupUtils.getCurrentIframePopup();
	var popupId = popup.getId();
	
	GlassLayerPopupsManager.closePopupAndReloadParent(popupId, reloadUrl);
}

/**
 * Asks all open popups to close
 */
GlassLayerPopupsManager.closeAllPopups = function() {
	var sharedData = GlassLayerPopupUtils.getSharedData();

	for (var popupId in sharedData.popups) {
		GlassLayerPopupsManager.closePopup(popupId);
	}
	
};	

/**
 * Asks a popup with given id to maximize
 */
GlassLayerPopupsManager.maximizePopup = function(popupId) {
	var sharedData = GlassLayerPopupUtils.getSharedData();
	var popup = sharedData.popups[popupId];
	popup.maximize();
}


/**
 * Returns true if current window belongs to iframe popup.
 * NOTE: the name of this method is likely to be changed while under alpha development. Sorry for this. 
 */
GlassLayerPopupsManager.isInsideIframePopup = function() {
	var currentWindowName = window.name; // get's the name of the iframe
	return currentWindowName.indexOf(GlassLayerPopupConstants.IFRAME_NAME_PREFIX) === 0;
}

/**
 * When popup is shown using showUrl() than we loose the reference to actual instance of GlassLayerPopup
 * and because of this it is hard to perform operations on popup. This method
 * comes as a convenience method which returns a reference to the correct instance of GlassLayerPopup inside
 * which GlassLayerPopupUtils.getCurrentPopup() is called. If this method is not called inside any GlassLayerPopup 
 * created by GlassLayerPopup.showUrl() but by showHtml() than it returns null. 
 * 
 * NOTE: from above there is one conculsion do not call this method if you created popup using 
 * GlassLayerPopup.showHtml(). In this case you still have reference to the popup since the call
 * was made inside the same window.
 * 
 * @returns GlassLayerPopup
 */
GlassLayerPopupsManager.getCurrentIframePopup = function() {
	var currentWindowName = window.name; // get's the name of the iframe
	
	if (currentWindowName.indexOf(GlassLayerPopupConstants.IFRAME_NAME_PREFIX) !== 0) {
		throw "Trying to call GlassLayerPopupUtils.getCurrentPopup() inside a frame/window which is not a popup frame " +
				" It's possible that you have nested an iframe inside the popup and trying to call this method from " +
				" inside. This is not supported yet.";
	}
	
	var popupId = GlassLayerPopupUtils.parseIdFromPopupIframeName(currentWindowName);
	var popup = GlassLayerPopupsManager.getPopup(popupId);
	
	return popup;
	
};




/**
 * Obtains a reference to the html popup inside which element passed as a 
 * parameter is located. Usually should be called using 
 * GlassLayerPopupUtils.getCurrentHtmlPopup(this)
 */
GlassLayerPopupsManager.getHtmlPopupWithElement = function(element) {
	if (element == null || element == undefined) {
		throw 'Trying to get html popup relative to element which is null or undefined';
	}
	
	var popupMainDiv = GlassLayerPopupDOMUtils.findParentWithName(element, GlassLayerPopupConstants.MAIN_DIV_NAME);
	if (parent == null) {
		throw "Trying to call GlassLayerPopupUtils.getCurrentHtmlPopup() for element "
			+ " which is not located on popup. element is: " + currentElement 
			+ " HINT: when using this and calling from <a> element use onclick instead of href='javascript'";
	}
	
	var mainDivId = popupMainDiv.getAttribute("id");
	var popupId = GlassLayerPopupUtils.parseIdFromMainDivId(mainDivId);
	var popup = GlassLayerPopupsManager.getPopup(popupId);
	
	return popup;
}



  // ======================================== //
 //  Definition of GlassLayerPopupDOMUtils   //
//========================================= //


/**
 * @private
 * 
 * This class contains methods which operates on DOM elements. Was added for
 * convenience in order not to bother external libraries
 * @return
 */
function GlassLayerPopupDOMUtils() {}

/**
 * Inspects all childs of given parent element and returns child element 
 * with given id. If such child is not found null is returned.
 */
GlassLayerPopupDOMUtils.findChildWithId = function(parentElement, childId) {
	var childElement = null;
	for (var i = 0; i < parentElement.childNodes.length; i++) {
		var curChild = parentElement.childNodes[i];
		if (curChild.id == childId) {
			childElement = curChild;
			break;
		}
	}
	
	return childElement;
};

/**
 * Goes up in dom tree to find an element with name given as a parameter. If such
 * element is not found null is returned
 */
GlassLayerPopupDOMUtils.findParentWithName = function(currentElement, parentName) {
	var parentElem = null;
	
	var canAccessParent = true;
	var curParent = null;
	
	while(canAccessParent && parentElem == null) {
		try {
			var curParent = currentElement.parentNode;
			if (curParent == currentElement) {
				// if parent points to the same element than somethig is wrong... 
				canAccessParent = false;
			} else if (currentElement.getAttribute && currentElement.getAttribute("name") == parentName) {
				parentElem = currentElement
			}
			currentElement = curParent;
		} catch (e) {
			// there was no parrent element or we could not access the parnet
			// so we should stop looping since current window is top accesible
			canAccessParent = false;
		}
	}
	return parentElem;

};
